/*!
  \file render.c
 
  \brief Nviz library -- GLX context manipulation
  
  COPYRIGHT: (C) 2008 by the GRASS Development Team

  This program is free software under the GNU General Public
  License (>=v2). Read the file COPYING that comes with GRASS
  for details.

  Based on visualization/nviz/src/togl.c

  \author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008)

  \date 2008
*/

#include <grass/glocale.h>
#include <grass/nviz.h>

/*!
  \brief Allocate memory for render window

  \return pointer to render_window struct
  \return NULL on failure
*/
struct render_window *Nviz_new_render_window(void)
{
  struct render_window *rwin;

  /* G_malloc() calls G_fatal_error() on failure */
  rwin = (struct render_window *) G_malloc (sizeof (struct render_window));
  
  return rwin;
}
					    
/*!
  \brief Initialize render window

  \param win pointer to render_window struct
*/
void Nviz_init_render_window(struct render_window *rwin)
{
#if defined(OPENGL_X11)
    rwin->displayId = NULL;
    rwin->contextId = NULL;
    rwin->pixmap = 0;
    rwin->windowId = 0;
#elif defined(OPENGL_AQUA)
    rwin->pixelFmtId = NULL;
    rwin->contextId = NULL;
    rwin->pixmap = 0;
    rwin->windowId = 0;
#elif defined(OPENGL_WINDOWS)
    rwin->displayId = NULL;
    rwin->contextId = NULL;
    rwin->bitmapId = NULL;
#endif
}

/*!
  \brief Free render window

  \param win pointer to render_window struct
*/
void Nviz_destroy_render_window(struct render_window *rwin)
{
#if defined(OPENGL_X11)
    glXDestroyContext(rwin->displayId, rwin->contextId);
    glXDestroyGLXPixmap(rwin->displayId, rwin->windowId); 
    XFreePixmap(rwin->displayId, rwin->pixmap);
#elif defined(OPENGL_AQUA)
    aglDestroyContext(rwin->contextId);
    aglDestroyAGLPixmap(rwin->windowId); 
    /* TODO FreePixMap */
#elif defined(OPENGL_WINDOWS)
    wglDeleteContext(rwin->contextId);
    DeleteDC(rwin->displayId);
    DeleteObject(rwin->bitmapId);
#endif

    G_free ((void *) rwin);
}

/*!
  \brief Create render window

  \param rwin pointer to render_window struct
  \param display display instance (NULL for offscreen)
  \param width window width
  \param height window height

  \return 1
*/
int Nviz_create_render_window(struct render_window *rwin, void *display,
			      int width, int height)
{
#if defined(OPENGL_X11)
    int attributeList[] = { GLX_RGBA, GLX_RED_SIZE, 1,
			    GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1,
			    GLX_DEPTH_SIZE, 1, None };
    XVisualInfo  *v;

    rwin->displayId = XOpenDisplay((char *) display);
    if (!rwin->displayId) {
	G_fatal_error (_("Bad server connection"));
    }

    v = glXChooseVisual(rwin->displayId,
			DefaultScreen(rwin->displayId),
			attributeList);

    rwin->contextId = glXCreateContext(rwin->displayId,
				       v, NULL, GL_FALSE);

    if (!rwin->contextId) {
	G_fatal_error (_("Unable to create rendering context"));
    }

    /* create win pixmap to render to (same depth as RootWindow) */
    rwin->pixmap = XCreatePixmap(rwin->displayId,
				RootWindow(rwin->displayId, v->screen),
				width,
				height,
				v->depth);

    /* create an off-screen GLX rendering area */
    rwin->windowId = glXCreateGLXPixmap(rwin->displayId,
					v, rwin->pixmap);

    if (v) {
	XFree(v);
    }
#elif defined(OPENGL_AQUA)
    int attributeList[] = { AGL_RGBA, AGL_RED_SIZE, 1,
			    AGL_GREEN_SIZE, 1, AGL_BLUE_SIZE, 1,
			    AGL_DEPTH_SIZE, 1, AGL_NONE };
    /* TODO: open mac display */

    /* TODO: dev = NULL, ndev = 0 ? */
    rwin->pixelFmtId = aglChoosePixelFmt(NULL, 0, attributeList);
    
    rwin->contextId = aglCreateContext(rwin->pixelFmtId, NULL); 

    /* create win pixmap to render to (same depth as RootWindow) */
    rwin->pixmap = NULL; /* TODO: create GWorldPtr */
    /* create an off-screen AGL rendering area */
    rwin->windowId = aglCreateAGLPixmap(rwin->pixelFmtId,
					rwin->pixmap); 
#elif defined(OPENGL_WINDOWS)
    PIXELFORMATDESCRIPTOR pfd = { 
	    sizeof(PIXELFORMATDESCRIPTOR),  //  size of this pfd 
	    1,                     /* version number           */ 
	    PFD_DRAW_TO_WINDOW |   /* support window 	       */
	    PFD_SUPPORT_OPENGL |   /* support OpenGL 	       */
	    PFD_DOUBLEBUFFER,      /* double buffered 	       */
	    PFD_TYPE_RGBA,         /* RGBA type 	       */
	    24,                    /* 24-bit color depth       */
	    0, 0, 0, 0, 0, 0,      /* color bits ignored       */
	    0,                     /* no alpha buffer 	       */
	    0,                     /* shift bit ignored        */
	    0,                     /* no accumulation buffer   */
	    0, 0, 0, 0,            /* accum bits ignored       */
	    32,                    /* 32-bit z-buffer          */
	    0,                     /* no stencil buffer        */
	    0,                     /* no auxiliary buffer      */
	    PFD_MAIN_PLANE,        /* main layer 	       */
	    0,                     /* reserved 		       */
	    0, 0, 0                /* layer masks ignored      */
    };
    int iPixelFormat;

    rwin->displayId = CreateCompatibleDC(NULL);
    iPixelFormat = ChoosePixelFormat(rwin->displayId, &pfd);
    SetPixelFormat(rwin->displayId, iPixelFormat, &pfd); 
    rwin->bitmapId = CreateCompatibleBitmap(rwin->displayId, width, height);
    SelectObject(rwin->displayId, rwin->bitmapId);
    rwin->contextId = wglCreateContext(rwin->displayId); 
    /* TODO */
#endif
    return 1;
}

/*!
  \brief Make window current for rendering

  \param win pointer to render_window struct

  \return 1 on success
  \return 0 on failure
*/
int Nviz_make_current_render_window(const struct render_window *rwin)
{
#if defined(OPENGL_X11)
    if (!rwin->displayId || !rwin->contextId)
	return 0;

    if (rwin->contextId == glXGetCurrentContext())
	return 1;

    glXMakeCurrent(rwin->displayId, rwin->windowId,
		   rwin->contextId);
#elif defined(OPENGL_AQUA)
    if (rwin->contextId == aglGetCurrentContext())
	return 1;

    /* TODO: mac_win */
    aglMakeCurrent((AGLDrawable) mac_win, rwin->contextId);
#elif defined(OPENGL_WINDOWS)
    if (!rwin->displayId || !rwin->contextId)
	return 0;

    wglMakeCurrent(rwin->displayId, rwin->contextId);
#endif

    return 1;
}
